from __future__ import division

import math

from ..attributes import *
from ..attributes import _

from .information import *
from .rendering import *


class TextStyle(Style):
    alignment  = Attribute(initial = 'left', writable = True)
    highlight  = Attribute(initial = None,   writable = True)
    collapsing = Attribute(initial = False,  writable = True)
    padding    = Attribute(initial = 1,      writable = True)
    
    def size(self, text):
        return (len(text) + self.padding * 2, 1)
    
    @property
    def line_start(self):
        if self.highlight is None or len(self.highlight) == 0:
            return ' '
        else:
            return self.highlight[0]
    
    @property
    def line_end(self):
        if self.highlight is None or len(self.highlight) == 0:
            return ' '
        elif len(self.highlight) == 1:
            return self.highlight[0]
        else:
            return self.highlight[1]
    
    @property
    def alignment_char(self):
        if self.alignment == 'right':
            return '>'
        elif self.alignment == 'center':
            return '^'
        else:
            return '<'
    
    def format_spec(self, length):
        return '{0:' + self.alignment_char + str(length) + '}'
    
    def format_text(self, area, text):
        length = area.width - 2
        lines  = int(math.ceil(len(text.strip()) / length))
        result = ''
        
        for num in range(lines):
            result += self.format_line(length, text[num * length:(num + 1) * length])
        
        return result
    
    def format_line(self, length, line):
        if self.padding == 0:
            return self.format_spec(length).format(line)
        elif self.collapsing:
            return (' ' * (self.padding - 1) +
                    self.format_spec(length - (self.padding - 1) * 2).format(self.line_start + line + self.line_end) +
                    ' ' * (self.padding - 1))
        else:
            return (self.line_start + ' ' * (self.padding - 1) +
                    self.format_spec(length - (self.padding - 1) * 2).format(line) +
                    ' ' * (self.padding - 1) + self.line_end)


class TextPrimitives(InformationPrimitiveSource):
    def create_text(self, element):
        return TextPrimitive(element, self.renderer, lambda: element.text)

    def create_image(self, element):
        return TextPrimitive(element, self.renderer, '[I]')
    
    def create_button(self, element):
        return TextPrimitive(element, self.renderer, '[B]')


class TextPrimitive(InformationPrimitive):
    renderer = Attribute()
    data     = Attribute()
    area     = Attribute(writable = True)
    
    def setup(self):
        self.renderer.add_primitive(self)
    
    @property
    def intrinsic_size(self):
        return self.renderer.style_for(self).size(self.data())

    def set_area(self, area):
        self.area = area
        self.update()
        
    def update(self):
        style = self.renderer.style_for(self)
        text  = style.format_text(self.area, self.data())
        
        self.renderer.write(self.area, text)


class TextRenderingArea(RenderingArea):
    primitive_source =_ (TextPrimitives)
    default_style    =_ (TextStyle)
    
    data       = ListAttribute(initiable = False)
    primitives = ListAttribute(initiable = False)
    
    def setup(self):
        self.clear()
    
    def add_primitive(self, primitive):
        self._primitives.append(primitive)
    
    def clear(self):
        self._data       = []
        self._primitives = []
        
        for y in range(self.height):
            self._data.append([])
            
            for x in range(self.width):
                self._data[y].append(' ')
    
    def get(self, pos):
        return self._data[pos.y][pos.x]
    
    def put(self, pos, character):
        self._data[pos.y][pos.x] = character
    
    def write(self, area, text):
        for num, position in enumerate(area):
            if not self.area.includes(position):
                continue
            
            if num >= len(text):
                self.put(position, ' ')
            else:
                self.put(position, text[num])
    
    def update(self):
        for primitive in self.primitives:
            primitive.update()
    
    def __str__(self):
        return '\n'.join(''.join(line) for line in self._data)
